package com.studyweb.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 文件上传工具类
 * 
 * @author StudyWeb Team
 * @since 1.0.0
 */
@Slf4j
@Component
public class FileUploadUtils {
    
    @Autowired
    private FileUploadProperties fileUploadProperties;
    
    @Autowired
    private AliyunOssProperties ossProperties;
    
    @Autowired
    private AliyunOssUtils aliyunOssUtils;
    
    /**
     * 上传文件
     * 优先尝试OSS上传，失败时自动转为本地上传
     * 
     * @param file 上传的文件
     * @return 上传结果信息
     * @throws IOException IO异常
     */
    public Map<String, Object> uploadFile(MultipartFile file) throws IOException {
        log.info("开始文件上传处理: {}, OSS启用状态: {}", file.getOriginalFilename(), ossProperties.isEnabled());
        
        // 如果启用了OSS，先尝试OSS上传
        if (ossProperties.isEnabled()) {
            try {
                log.info("尝试上传文件到阿里云OSS: {}", file.getOriginalFilename());
                Map<String, Object> result = aliyunOssUtils.uploadFile(file);
                log.info("文件上传到OSS成功: {}", file.getOriginalFilename());
                return result;
            } catch (Exception e) {
                log.warn("阿里云OSS上传失败，自动转为本地上传: {}, 错误: {}", file.getOriginalFilename(), e.getMessage());
                // OSS上传失败，转为本地上传
                return uploadFileLocalWithFallback(file, "OSS上传失败，已自动转为本地上传");
            }
        } else {
            // 直接使用本地上传
            log.info("使用本地上传: {}", file.getOriginalFilename());
            Map<String, Object> result = uploadFileLocal(file);
            log.info("本地上传完成，上传方式: {}", result.get("uploadMethod"));
            return result;
        }
    }
    
    /**
     * 本地文件上传
     * 
     * @param file 上传的文件
     * @return 上传结果信息
     * @throws IOException IO异常
     */
    private Map<String, Object> uploadFileLocal(MultipartFile file) throws IOException {
        Map<String, Object> result = new HashMap<>();
        
        // 检查文件是否为空
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("上传文件不能为空");
        }
        
        // 获取原始文件名
        String originalFileName = file.getOriginalFilename();
        if (originalFileName == null || originalFileName.trim().isEmpty()) {
            throw new IllegalArgumentException("文件名不能为空");
        }
        
        // 检查文件类型
        if (!fileUploadProperties.isAllowedType(originalFileName)) {
            throw new IllegalArgumentException("不支持的文件类型");
        }
        
        // 检查文件大小
        long fileSize = file.getSize();
        long maxSizeBytes = fileUploadProperties.getMaxSize() * 1024 * 1024; // 转换为字节
        if (fileSize > maxSizeBytes) {
            throw new IllegalArgumentException("文件大小超过限制");
        }
        
        // 生成新的文件名
        String fileExtension = fileUploadProperties.getFileExtension(originalFileName);
        String newFileName = generateFileName(fileExtension);
        
        // 创建存储目录
        String uploadPath = fileUploadProperties.getPath();
        String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        String fullPath = uploadPath + datePath + "/";
        
        File directory = new File(fullPath);
        if (!directory.exists()) {
            boolean created = directory.mkdirs();
            if (!created) {
                throw new IOException("创建存储目录失败");
            }
        }
        
        // 保存文件
        String filePath = fullPath + newFileName;
        Path targetPath = Paths.get(filePath);
        Files.copy(file.getInputStream(), targetPath);
        
        // 构建访问URL
        String accessUrl = fileUploadProperties.getUrlPrefix() + datePath + "/" + newFileName;
        
        // 返回结果
        result.put("uploadMethod", "local");
        log.info(">>> DEBUG: 设置 uploadMethod = local <<<");
        result.put("originalName", originalFileName);
        result.put("fileName", newFileName);
        result.put("filePath", filePath);
        result.put("accessUrl", accessUrl);
        result.put("fileSize", fileSize);
        result.put("fileType", file.getContentType());
        result.put("fileExtension", fileExtension);
        
        log.info("★★★ 文件上传成功（强制更新版本）：{} -> {} ★★★", originalFileName, filePath);
        log.info(">>> DEBUG: 返回结果包含 uploadMethod: {} <<<", result.get("uploadMethod"));
        
        return result;
    }
    
    /**
     * 本地文件上传（带备注信息）
     * 
     * @param file 上传的文件
     * @param remark 备注信息
     * @return 上传结果信息
     * @throws IOException IO异常
     */
    private Map<String, Object> uploadFileLocalWithFallback(MultipartFile file, String remark) throws IOException {
        Map<String, Object> result = uploadFileLocal(file);
        // 添加备注信息，表示这是由于OSS失败后的备用方案
        result.put("uploadMethod", "local");
        result.put("fallbackReason", remark);
        log.info("备用方案上传成功: {} -> {}", file.getOriginalFilename(), result.get("filePath"));
        return result;
    }
    
    /**
     * 删除文件
     * 根据上传方式删除文件
     * 
     * @param filePath 文件路径
     * @param uploadMethod 上传方式（oss/local）
     * @return 是否删除成功
     */
    public boolean deleteFile(String filePath, String uploadMethod) {
        // 根据记录的上传方式来决定删除方式
        if ("oss".equals(uploadMethod)) {
            try {
                boolean ossResult = aliyunOssUtils.deleteFile(filePath);
                if (ossResult) {
                    log.info("文件从OSS删除成功: {}", filePath);
                    return true;
                } else {
                    log.warn("OSS删除失败: {}", filePath);
                    return false;
                }
            } catch (Exception e) {
                log.error("OSS删除异常: {}, 错误: {}", filePath, e.getMessage());
                return false;
            }
        } else {
            // 本地删除
            return deleteFileLocal(filePath);
        }
    }
    
    /**
     * 删除文件（兼容旧版本方法）
     * 优先尝试OSS删除，失败时自动转为本地删除
     * 
     * @param filePath 文件路径
     * @return 是否删除成功
     */
    public boolean deleteFile(String filePath) {
        // 如果启用了OSS，先尝试OSS删除
        if (ossProperties.isEnabled()) {
            try {
                boolean ossResult = aliyunOssUtils.deleteFile(filePath);
                if (ossResult) {
                    log.info("文件从OSS删除成功: {}", filePath);
                    return true;
                } else {
                    log.warn("OSS删除失败，尝试本地删除: {}", filePath);
                    return deleteFileLocal(filePath);
                }
            } catch (Exception e) {
                log.warn("OSS删除异常，转为本地删除: {}, 错误: {}", filePath, e.getMessage());
                return deleteFileLocal(filePath);
            }
        } else {
            // 直接使用本地删除
            return deleteFileLocal(filePath);
        }
    }
    
    /**
     * 本地文件删除
     * 
     * @param filePath 文件路径
     * @return 是否删除成功
     */
    private boolean deleteFileLocal(String filePath) {
        if (filePath == null || filePath.trim().isEmpty()) {
            return false;
        }
        
        try {
            Path path = Paths.get(filePath);
            boolean deleted = Files.deleteIfExists(path);
            
            if (deleted) {
                log.info("文件删除成功：{}", filePath);
            } else {
                log.warn("文件不存在或删除失败：{}", filePath);
            }
            
            return deleted;
        } catch (IOException e) {
            log.error("删除文件异常：{}", filePath, e);
            return false;
        }
    }
    
    /**
     * 检查文件是否存在
     * 优先检查OSS，失败时自动转为本地检查
     * 
     * @param filePath 文件路径
     * @return 是否存在
     */
    public boolean fileExists(String filePath) {
        // 如果启用了OSS，先尝试OSS检查
        if (ossProperties.isEnabled()) {
            try {
                return aliyunOssUtils.fileExists(filePath);
            } catch (Exception e) {
                log.warn("OSS检查文件存在性异常，转为本地检查: {}, 错误: {}", filePath, e.getMessage());
                return fileExistsLocal(filePath);
            }
        } else {
            // 直接使用本地检查
            return fileExistsLocal(filePath);
        }
    }
    
    /**
     * 本地文件存在性检查
     * 
     * @param filePath 文件路径
     * @return 是否存在
     */
    private boolean fileExistsLocal(String filePath) {
        if (filePath == null || filePath.trim().isEmpty()) {
            return false;
        }
        
        try {
            Path path = Paths.get(filePath);
            return Files.exists(path);
        } catch (Exception e) {
            log.error("检查文件存在性异常：{}", filePath, e);
            return false;
        }
    }
    
    /**
     * 生成唯一文件名
     * 
     * @param fileExtension 文件扩展名
     * @return 新文件名
     */
    private String generateFileName(String fileExtension) {
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
        return timestamp + "_" + uuid + fileExtension;
    }
    
    /**
     * 获取文件内容
     * 优先尝试从OSS获取，失败时自动转为本地获取
     * 
     * @param filePath 文件路径
     * @return 文件内容字节数组
     * @throws IOException IO异常
     */
    public byte[] getFileContent(String filePath) throws IOException {
        // 如果启用了OSS，先尝试从OSS获取
        if (ossProperties.isEnabled()) {
            try {
                byte[] content = aliyunOssUtils.getFileContent(filePath);
                if (content != null) {
                    log.info("从OSS获取文件内容成功: {}, 大小: {} bytes", filePath, content.length);
                    return content;
                } else {
                    log.warn("OSS文件内容为空，尝试本地获取: {}", filePath);
                    return getFileContentLocal(filePath);
                }
            } catch (Exception e) {
                log.warn("OSS获取文件内容异常，转为本地获取: {}, 错误: {}", filePath, e.getMessage());
                return getFileContentLocal(filePath);
            }
        } else {
            // 直接使用本地获取
            return getFileContentLocal(filePath);
        }
    }
    
    /**
     * 本地文件内容获取
     * 
     * @param filePath 文件路径
     * @return 文件内容字节数组
     */
    private byte[] getFileContentLocal(String filePath) {
        if (filePath == null || filePath.trim().isEmpty()) {
            return null;
        }
        
        try {
            Path path = Paths.get(filePath);
            if (!Files.exists(path)) {
                log.warn("文件不存在：{}", filePath);
                return null;
            }
            
            byte[] content = Files.readAllBytes(path);
            log.info("本地文件内容获取成功：{}, 大小：{} bytes", filePath, content.length);
            return content;
            
        } catch (IOException e) {
            log.error("获取本地文件内容异常：{}", filePath, e);
            return null;
        }
    }
    
    /**
     * 检查OSS是否启用
     * 
     * @return 是否启用
     */
    public boolean isOssEnabled() {
        return ossProperties.isEnabled();
    }
    
    /**
     * 格式化文件大小
     * 
     * @param size 文件大小（字节）
     * @return 格式化后的文件大小
     */
    public String formatFileSize(long size) {
        if (size < 1024) {
            return size + " B";
        } else if (size < 1024 * 1024) {
            return String.format("%.2f KB", size / 1024.0);
        } else if (size < 1024 * 1024 * 1024) {
            return String.format("%.2f MB", size / (1024.0 * 1024.0));
        } else {
            return String.format("%.2f GB", size / (1024.0 * 1024.0 * 1024.0));
        }
    }
}
